package com.kedacom.demo.cometd;

import java.util.HashMap;
import java.util.Map;

import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;

import org.cometd.bayeux.Message;
import org.cometd.bayeux.client.ClientSessionChannel;
import org.cometd.client.BayeuxClient;
import org.cometd.client.transport.ClientTransport;
import org.cometd.client.transport.LongPollingTransport;
import org.cometd.common.Jackson2JSONContextClient;
import org.cometd.websocket.client.WebSocketTransport;
import org.eclipse.jetty.client.HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.json.JSONObject;

/**
 * CometdJavaClient
 * @author luocanfeng
 * @date 2016年4月15日
 */
public class CometdJavaClient {

	private static Logger logger = LoggerFactory.getLogger(CometdJavaClient.class);

	private static BayeuxClient bayeuxClient;

	/**
	 * 获取BayeuxClient
	 */
	public static BayeuxClient getBayeuxClient() {
		if (bayeuxClient == null) {
			synchronized (CometdJavaClient.class) {
				if (bayeuxClient == null) {
					try {
						initBayeuxClient();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
		return bayeuxClient;
	}

	/**
	 * 初始化BayeuxClient
	 */
	private static void initBayeuxClient() throws Exception {
		Map<String, Object> transportOptions = new HashMap<String, Object>();
		// Use the Jackson implementation
		transportOptions.put(ClientTransport.JSON_CONTEXT_OPTION, new Jackson2JSONContextClient());

		// Prepare the Jetty WebSocket transport
		// WebSocketClient webSocketClient = new WebSocketClient();
		// webSocketClient.start();
		// ClientTransport wsTransport = new
		// JettyWebSocketTransport(transportOptions, null, webSocketClient);

		// Prepare the JSR 356 WebSocket transport
		WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer();
		// The WebSocketContainer must be started, but JSR 356 APIs do not
		// define any lifecycle APIs, so a Jetty specific cast would be
		// required.
		// However, this is avoidable by piggybacking on HttpClient like shown
		// below.
		// ((LifeCycle)webSocketContainer).start();

		ClientTransport wsTransport = new WebSocketTransport(transportOptions, null, webSocketContainer);
		// Prepare the HTTP transport
		HttpClient httpClient = new HttpClient();
		httpClient.addBean(webSocketContainer, true);
		httpClient.start();
		ClientTransport httpTransport = new LongPollingTransport(transportOptions, httpClient);

		// Configure the BayeuxClient, with the websocket transport listed
		// before the http transport
		bayeuxClient = new BayeuxClient(Constants.COMETD_SERVER_URL, wsTransport, httpTransport);

		// Handshake
		bayeuxClient.handshake();

		for (int i = 0; i < 15; i++) {
			if (bayeuxClient.waitFor(1000, BayeuxClient.State.CONNECTED)) {
				logger.debug("Handshake success.");
				return;
			}
		}
		throw new RuntimeException("Wait timeout for handshake.");
	}

	/**
	 * 发送cometd消息
	 */
	public static void sendMessage(String channel, Object message) {
		getBayeuxClient().getChannel(channel).publish(message, messageSenderListener);
	}

	private static ClientSessionChannel.MessageListener messageSenderListener = new MessageSenderListener();

	/**
	 * 发送cometd消息Callback
	 */
	public static class MessageSenderListener implements ClientSessionChannel.MessageListener {

		@Override
		public void onMessage(ClientSessionChannel channel, Message message) {
			if (message.isSuccessful()) {
				logger.debug("Send message success, message is: {}", message.getJSON());
			} else {
				logger.error("Send message failed, message is: {}", message.getJSON());
			}
		}
	}

	/**
	 * 监听cometd meta消息
	 */
	public static class MetaChannelsMessageListener implements ClientSessionChannel.MessageListener {

		@Override
		public void onMessage(ClientSessionChannel channel, Message message) {
			logger.debug("Receiver meta channel message: {}", JSONObject.fromObject(message).toString());
		}
	}

}
